//
// Created by shubin on 10.04.24.
//
#pragma once
#include <GL/glut.h>

class GlWindow {
private:
    GLint hWnd;
    int width;
    int height;
public:

    GlWindow(int x, int y, int width, int height, const char * title) {
        glutInitWindowPosition(x, y);
        glutInitWindowSize(width, height);
        hWnd = glutCreateWindow(title);

        this->width = glutGet(GLUT_WINDOW_WIDTH);
        this->height = glutGet(GLUT_WINDOW_HEIGHT);
    }

    inline int get_width() const {
        return width;
    }

    inline int get_height() const {
        return height;
    }

};


class GlApplication {
private:
    void attach();
    GlWindow * window = nullptr;
    bool _2d;
protected:
    virtual GlWindow * createWindow()  = 0;
    virtual void createResources() { }
    virtual void releaseResources() { }
    virtual void create_scene() { }
    virtual void pre_loop() { }
public:
    GlApplication(int argc, char **argv, bool _2d) {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA |  GLUT_DEPTH);
        this->_2d = _2d;
    }

    ~GlApplication() {
        delete this->window;
    }


    void init_timer(int timer_id, int interval);

    virtual void run() {
        this->window = this->createWindow();
        this->createResources();
        this->attach();
        pre_loop();
        create_scene();
        glutMainLoop();
        this->releaseResources();
    }

    void on_reshape(int width, int height) const {
        // двухмерное окно вывода
        glViewport(0, 0, width, height);
        // ортогональная проекция
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        //gluOrtho2D(0, width, height, 0);
        if (this->_2d)
            glOrtho(0, width, height, 0, -100, 100);
        else {
            if (height == 0) height = 1;                // To prevent divide by 0
            GLfloat aspect = (GLfloat)width / (GLfloat)height;
            gluPerspective(45.0f, aspect, 0.1f, 100.0f);
        }
    }

    virtual void on_timer(int id) {

    }

    virtual void on_key_down(int key, bool spec, int x, int y) {
        switch (key) {
            case 27:
                delete this;
                exit(0);
        }
    }

    virtual void on_motion(int x, int y) {

    }

    virtual void on_mouse(int button, int state, int x, int y) {

    }

    virtual void draw() {

    }
};